﻿using ImageMagick;
using Microsoft.AspNetCore.Http;
using NLog;
using SkiaSharp;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using static EssentialWebFramework.Utils.ImageUtils;

namespace EssentialWebFramework.Utils
{
    public class ImageUtils
    {
        /// <summary>
        /// 图片最大大小（10M）
        /// </summary>
        private const long IMAGE_MAX_SIZE = 10 * 1024 * 1024;

        /// <summary>
        /// Logger
        /// </summary>
        private static readonly Logger Logger = LogManager.GetCurrentClassLogger();

        /// <summary>
        /// 判断是否为图片
        /// </summary>
        /// <param name="fileFullPath"></param>
        /// <returns></returns>
        public static bool IsImage(string fileFullPath)
        {
            using (SKImage skImage = SKImage.FromEncodedData(SKData.Create(fileFullPath)))
            {
                int width;
                try
                {
                    width = skImage.Width;
                    return width > 0;
                }
                catch
                {
                    return false;
                }
            }
        }

        /// <summary>
        /// 压缩图像文件
        /// </summary>
        /// <param name="imageFileFullName"></param>
        /// <param name="isCreateNewFile">压缩后是否创建新的文件</param>
        /// <param name="imageMaxSize"></param>
        /// <returns></returns>
        public static string CompressImageFile(string imageFileFullName, 
            bool isCreateNewFile, long imageMaxSize = IMAGE_MAX_SIZE)
        {
            try
            {
                int quality = 100;
                int maxLoopCount = 5;

                FileInfo imageFileInfo = new FileInfo(imageFileFullName);

                FileInfo targetFileInfo;
                if (!isCreateNewFile)
                {
                    targetFileInfo = imageFileInfo;
                }
                else
                {
                    string newImageFileName = CommonUtils.AppendToFileName(imageFileFullName, "_compressed");
                    FileInfo newImageFileInfo = new FileInfo(newImageFileName);
                    imageFileInfo.CopyTo(newImageFileName, true);
                    targetFileInfo = newImageFileInfo;
                }

                do
                {
                    // -(0.4x - 1.2)^2 + 4，将递减叠加起来，并使用反比例函数，适当增加迭代次数以避免单次压缩过多
                    quality -= (int)(5 * (-Math.Pow(0.4 * maxLoopCount - 1.2, 2) + 4));

                    FileInfo tempFile = DoCompressImage(targetFileInfo, quality);   // 由于是往同一个文件写，暂不使用异步方法

                    // 压缩后体积变得更大了，则删除临时文件并终止循环
                    if (tempFile.Length >= targetFileInfo.Length)
                    {
                        tempFile.Delete();
                        break;
                    }


                    tempFile.MoveTo(targetFileInfo.FullName, true);
                    targetFileInfo.Refresh();

                    // 如果压缩后的图片体积满足限制了，则终止循环
                    if (tempFile.Length <= imageMaxSize)
                    {
                        break;
                    }

                    maxLoopCount--;
                } while (targetFileInfo.Length >= imageMaxSize && maxLoopCount > 0);

                return targetFileInfo.FullName;
            }
            catch(Exception ex)
            {               
                Logger.Error(ex);
                return null;
            }            
        }

        /// <summary>
        /// 生成缩略图
        /// </summary>
        /// <param name="imageFileFullName"></param>
        /// <returns></returns>
        public static string GenerateImageThumbnail(string imageFileFullName)
        {
            try
            {
                string thumbFileFullName = CommonUtils.AppendToFileName(imageFileFullName, "_thumbnail");

                using (FileStream fs = new FileStream(thumbFileFullName, FileMode.OpenOrCreate))
                using (MagickImage magickImage = new MagickImage(new FileInfo(imageFileFullName)))
                {
                    magickImage.Resize(new Percentage(10));
                    magickImage.Write(fs);
                }

                return thumbFileFullName;
            }
            catch (Exception ex)
            {
                Logger.Error(ex);
                return null;
            }            
        }

        /// <summary>
        /// 压缩图片
        /// </summary>
        /// <param name="srcFile"></param>
        /// <param name="quality"></param>
        public static FileInfo DoCompressImage(FileInfo srcFile, int quality)
        {
            string tempFileName = CommonUtils.AppendToFileName(srcFile.FullName, "_compress_tmp");
            FileInfo result = new FileInfo(tempFileName);
            using (MagickImage magickImage = new MagickImage(srcFile))
            {
                magickImage.Quality = quality;
                magickImage.Write(result);
            }

            return result;
        }

        /// <summary>
        /// 生成缩略图
        /// </summary>
        /// <param name="file"></param>
        public static async Task<FileInfo> GenerageThumbnail(FileInfo sourceFile, FileInfo file)
        {
            using (FileStream fs = new FileStream(file.FullName, FileMode.OpenOrCreate))
            using (MagickImage magickImage = new MagickImage(sourceFile))
            {
                magickImage.Resize(new Percentage(10));
                await magickImage.WriteAsync(fs);
            }

            return new FileInfo(file.FullName);
        }

        /*

        /// <summary>
        /// 上传图片工具方法（带压缩）
        /// </summary>
        /// <param name="toUploadFiles">待上传的文件</param>
        /// <param name="category">分类（可选）</param>
        /// <param name="isGenerateThumbnail">是否生成缩略图</param>
        /// <param name="imageMaxSize">图片的最大大小</param>
        /// <returns></returns>
        public static async Task<List<ImageThumbnail>> HandleUploadImg(
            IEnumerable<IFormFile> toUploadFiles, string category = null, 
            bool isGenerateThumbnail = false, long imageMaxSize = IMAGE_MAX_SIZE)
        {
            string imagePath = string.IsNullOrEmpty(category) ?
                                    Path.Combine(BASE_DIRECTORY, IMAGE_ROOT_DIR, DateTime.Now.ToString("yyyy-MM")) :
                                    Path.Combine(BASE_DIRECTORY, IMAGE_ROOT_DIR, category, DateTime.Now.ToString("yyyy-MM"));
            DirectoryInfo imageDir = new DirectoryInfo(imagePath);
            if (!imageDir.Exists)
            {
                imageDir.Create();
            }

            List<ImageThumbnail> imageThumbnails = new List<ImageThumbnail>();

            try
            {
                foreach (var toUploadFile in toUploadFiles)
                {
                    string datetimeStr = DateTime.Now.ToString("yyyyMMddHHmmssfff") + CommonUtils.GenerateShortUniqueId();

                    ImageThumbnail imageThumbnail = new ImageThumbnail();
                    imageThumbnail.ImageName = string.Format("{0}_{1}", datetimeStr, toUploadFile.FileName);
                    string newFileFullName = Path.Combine(imagePath, imageThumbnail.ImageName);

                    using (FileStream fs = new FileStream(newFileFullName, FileMode.CreateNew))
                    {
                        await toUploadFile.CopyToAsync(fs);
                    }
                    FileInfo newFileInfo = new FileInfo(newFileFullName);
                    imageThumbnail.ImagePath = newFileInfo.FullName.Replace(BASE_DIRECTORY, "");
                    imageThumbnails.Add(imageThumbnail);    // 加入结果集中

                    if (!IsImage(newFileInfo))
                    {
                        CancelUploadImage(imageThumbnails);
                        return null;
                    }

                    int quality = 100;
                    int maxLoopCount = 7;

                    while (newFileInfo.Length >= imageMaxSize && maxLoopCount > 0)
                    {
                        // -(0.4x - 1.2)^2 + 4，将递减叠加起来，并使用反比例函数，适当增加迭代次数以避免单次压缩过多
                        quality -= (int)(4 * (-Math.Pow(0.4 * maxLoopCount - 1.2, 2) + 4));
                        FileInfo tmp = Compress(newFileInfo, quality);   // 由于是往同一个文件写，暂不使用异步方法

                        if(tmp.Length >= newFileInfo.Length)
                        {
                            tmp.Delete();
                            break;
                        }
                        else
                        {
                            newFileInfo.Delete();
                            tmp.MoveTo(newFileInfo.FullName);
                            newFileInfo = tmp;
                        }

                        maxLoopCount--;
                    }

                                       

                    if (isGenerateThumbnail)
                    {
                        string thumbnailFileFullName = AppendFileName(newFileFullName, "_thumbnail");
                        FileInfo newThumbnailFileInfo = await GenerageThumbnail(newFileInfo, new FileInfo(thumbnailFileFullName));
                        imageThumbnail.ThumbnailPath = newThumbnailFileInfo.FullName.Replace(BASE_DIRECTORY, "");   // 对已经加入结果集的当前元素赋值
                    }
                }

                imageThumbnails.ForEach(item =>
                {
                    item.ImagePath = CommonUtils.ConvertLocalFSPathToURLPart(item.ImagePath);
                    if (item.ThumbnailPath != null)
                    {
                        item.ThumbnailPath = CommonUtils.ConvertLocalFSPathToURLPart(item.ThumbnailPath);
                    }
                });
            }
            catch(Exception ex)
            {
                logger.Error(string.Format("图片处理出错：{0}，已取消该次上传，详情为：{1}", ex.Message, ex.StackTrace));
                
                CancelUploadImage(imageThumbnails);

                return null;
            }

            return imageThumbnails;
        }

        /// <summary>
        /// 遇到错误，取消本次所有上传
        /// </summary>
        /// <param name="imageThumbnails"></param>
        private static void CancelUploadImage(List<ImageThumbnail> imageThumbnails)
        {
            foreach (var imageThumbnail in imageThumbnails)
            {
                new FileInfo(Path.Combine(BASE_DIRECTORY, imageThumbnail.ImagePath)).Delete();
                if (imageThumbnail.ThumbnailPath != null)
                {
                    new FileInfo(Path.Combine(BASE_DIRECTORY, imageThumbnail.ThumbnailPath)).Delete();
                }
            }
        }

        /// <summary>
        /// 图片和缩略图
        /// </summary>
        public class ImageThumbnail
        {
            /// <summary>
            /// 图片名称
            /// </summary>
            public string ImageName { get; set; }
            /// <summary>
            /// 缩略图地址
            /// </summary>
            public string ThumbnailPath { get; set; }
            /// <summary>
            /// 图片地址
            /// </summary>
            public string ImagePath { get; set; }
        }

        /// <summary>
        /// 判断是否为图片
        /// </summary>
        /// <param name="fileFullPath"></param>
        /// <returns></returns>
        public static bool IsImage(string fileFullPath)
        {
            using (SKImage skImage = SKImage.FromEncodedData(SKData.Create(fileFullPath)))
            {
                int width;
                try
                {
                    width = skImage.Width;
                    return width > 0;
                }
                catch
                {
                    return false;
                }
            }
        }

        /// <summary>
        /// 压缩图片
        /// </summary>
        /// <param name="srcFile"></param>
        /// <param name="quality"></param>
        public static FileInfo DoCompressImage(FileInfo srcFile, int quality)
        {
            string tempFileName = CommonUtils.AppendToFileName(srcFile.FullName, "_compress_tmp");
            FileInfo result = new FileInfo(tempFileName);
            using (MagickImage magickImage = new MagickImage(srcFile))
            {
                magickImage.Quality = quality;
                magickImage.Write(result);
            }

            return result;
        }

        /// <summary>
        /// 生成缩略图
        /// </summary>
        /// <param name="file"></param>
        public static async Task<FileInfo> GenerageThumbnail(FileInfo sourceFile, FileInfo file)
        {
            using (FileStream fs = new FileStream(file.FullName, FileMode.OpenOrCreate))
            using (MagickImage magickImage = new MagickImage(sourceFile))            
            {
                magickImage.Resize(new Percentage(10));
                await magickImage.WriteAsync(fs);
            }

            return new FileInfo(file.FullName);
        }

        */
    }
}
